// ##########################################################################
// #                                                                        #
// #                              CLOUDCOMPARE                              #
// #                                                                        #
// #  This program is free software; you can redistribute it and/or modify  #
// #  it under the terms of the GNU General Public License as published by  #
// #  the Free Software Foundation; version 2 or later of the License.      #
// #                                                                        #
// #  This program is distributed in the hope that it will be useful,       #
// #  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
// #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          #
// #  GNU General Public License for more details.                          #
// #                                                                        #
// #          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
// #                                                                        #
// ##########################################################################

#ifndef CC_ASCII_OPEN_DIALOG_HEADER
#define CC_ASCII_OPEN_DIALOG_HEADER

// local
#include "qCC_io.h"

// Qt
#include <QDialog>
#include <QString>

// system
#include <vector>

enum CC_ASCII_OPEN_DLG_TYPES
{
	ASCII_OPEN_DLG_None   = 0,
	ASCII_OPEN_DLG_X      = 1,
	ASCII_OPEN_DLG_Y      = 2,
	ASCII_OPEN_DLG_Z      = 3,
	ASCII_OPEN_DLG_NX     = 4,
	ASCII_OPEN_DLG_NY     = 5,
	ASCII_OPEN_DLG_NZ     = 6,
	ASCII_OPEN_DLG_R      = 7,
	ASCII_OPEN_DLG_G      = 8,
	ASCII_OPEN_DLG_B      = 9,
	ASCII_OPEN_DLG_A      = 10,
	ASCII_OPEN_DLG_Rf     = 11,
	ASCII_OPEN_DLG_Gf     = 12,
	ASCII_OPEN_DLG_Bf     = 13,
	ASCII_OPEN_DLG_Af     = 14,
	ASCII_OPEN_DLG_Grey   = 15,
	ASCII_OPEN_DLG_RGB32i = 16, // RGBA as a single 32 bits integer (PCL style)
	ASCII_OPEN_DLG_RGB32f = 17, // RGBA as a single 32 bits float (PCL style)
	ASCII_OPEN_DLG_Label  = 18,
	ASCII_OPEN_DLG_QuatW  = 19,
	ASCII_OPEN_DLG_QuatX  = 20,
	ASCII_OPEN_DLG_QuatY  = 21,
	ASCII_OPEN_DLG_QuatZ  = 22,
	ASCII_OPEN_DLG_Scalar = 23, // should always be the last one! (see AsciiOpenDlg::CheckOpenSequence)
};

constexpr const unsigned ASCII_OPEN_DLG_TYPES_COUNT = ASCII_OPEN_DLG_Scalar + 1;

//! Default ASCII header columns
class AsciiHeaderColumns
{
  public:
	static QString X()
	{
		return "X";
	}
	static QString Y()
	{
		return "Y";
	}
	static QString Z()
	{
		return "Z";
	}
	static QString Nx()
	{
		return "Nx";
	}
	static QString Ny()
	{
		return "Ny";
	}
	static QString Nz()
	{
		return "Nz";
	}
	static QString R()
	{
		return "R";
	}
	static QString G()
	{
		return "G";
	}
	static QString B()
	{
		return "B";
	}
	static QString A()
	{
		return "A";
	}
	static QString Rf()
	{
		return "Rf";
	}
	static QString Gf()
	{
		return "Gf";
	}
	static QString Bf()
	{
		return "Bf";
	}
	static QString Af()
	{
		return "Af";
	}

	static QString Grey()
	{
		return "Grey";
	}
	static QString Scalar()
	{
		return "SF";
	}
	static QString RGB32i()
	{
		return "RGB32i";
	}
	static QString RGB32f()
	{
		return "RGB32f";
	}
	static QString Label()
	{
		return "Label";
	}
	static QString QuatW()
	{
		return "QuatW";
	}
	static QString QuatX()
	{
		return "QuatX";
	}
	static QString QuatY()
	{
		return "QuatY";
	}
	static QString QuatZ()
	{
		return "QuatZ";
	}
};

const char ASCII_OPEN_DLG_TYPES_NAMES[ASCII_OPEN_DLG_TYPES_COUNT][20] = {"Ignore",
                                                                         "coord. X",
                                                                         "coord. Y",
                                                                         "coord. Z",
                                                                         "Nx",
                                                                         "Ny",
                                                                         "Nz",
                                                                         "Red (0-255)",
                                                                         "Green (0-255)",
                                                                         "Blue (0-255)",
                                                                         "Alpha (0-255)",
                                                                         "Red.float (0-1)",
                                                                         "Green.float (0-1)",
                                                                         "Blue.float (0-1)",
                                                                         "Alpha.float (0-1)",
                                                                         "Grey",
                                                                         "RGBAi",
                                                                         "RGBAf",
                                                                         "Label",
                                                                         "Quaternion W",
                                                                         "Quaternion X",
                                                                         "Quaternion Y",
                                                                         "Quaternion Z",
                                                                         "Scalar"};

class QComboBox;
class QPushButton;
class QTextStream;
class Ui_AsciiOpenDialog;

//! Dialog for configuration of ASCII files opening sequence
class QCC_IO_LIB_API AsciiOpenDlg : public QDialog
{
	Q_OBJECT

  public:
	//! Default constructor
	/** \param parent parent widget
	 **/
	explicit AsciiOpenDlg(QWidget* parent = nullptr);

	//! Default destructor
	~AsciiOpenDlg() override;

	//! Sets the input filename or text stream
	/** \param filename filename
	    \param stream text stream
	    \return whether a previous context was saved or not and it can be safely applied again to this file
	**/
	bool setInput(const QString& filename, QTextStream* stream = nullptr);

	//! ASCII open sequence item
	struct SequenceItem
	{
		CC_ASCII_OPEN_DLG_TYPES type;
		QString                 header;

		//! Default constructor
		SequenceItem()
		    : type(ASCII_OPEN_DLG_None)
		    , header()
		{
		}

		//! Constructor from parameters
		SequenceItem(CC_ASCII_OPEN_DLG_TYPES _type, const QString& _header)
		    : type(_type)
		    , header(_header)
		{
		}
	};

	//! ASCII open sequence
	using Sequence = std::vector<SequenceItem>;

	//! Returns the whole "opening" sequence as set by the user
	Sequence getOpenSequence() const;

	//! Returns number of lines to skip
	unsigned getSkippedLinesCount() const;

	//! Returns user selected separator
	unsigned char getSeparator() const
	{
		return m_separator.cell();
	}

	//! Returns whether comma should be used as decimal point
	bool useCommaAsDecimal() const;

	//! Returns roughly estimated average line size (in bytes)
	double getAverageLineSize() const
	{
		return m_averageLineSize;
	}

	//! Returns columns count per line
	unsigned getColumnsCount() const
	{
		return m_columnsCount;
	}

	//! Returns the max number of points per cloud
	unsigned getMaxCloudSize() const;

	//! Whether labels should be visible in 2D
	bool showLabelsIn2D() const;

	//! Returns whether the current sequence is 'safe'
	/** A safe sequence is safe if it matches the header (if any)
	    or if the file has less than 6 columns.
	**/
	bool safeSequence() const;

	//! Checks the "opening" sequence as set by the user
	/** \return validity (+ error message if not)
	 **/
	static bool CheckOpenSequence(const Sequence& sequence, QString& errorMessage);

	//! Resets the "apply all" flag (if set)
	static void ResetApplyAll();

	//! Returns the quaternion scale
	double getQuaternionScale() const;

  public:
	//! Slot called when separator changes
	void onSeparatorChange(const QString& separator);
	//! Forces the table to update itself
	void updateTable();
	//! Slot called when  the number of lines to skip is changed
	void onSkippedLinesChanged(int);
	//! Slot called when the 'comma as decimal' checkbox is toggled
	void commaDecimalCheckBoxToggled(bool);

  protected:
	bool apply();
	void applyAll();
	void columnsTypeHasChanged(int index);
	void shortcutButtonPressed();
	void checkSelectedColumnsValidity();

  protected:
	//! Restores the previous context ('Apply all' button)
	/** \return whether a previous context was saved or not and it can be safely applied again to this file
	 **/
	bool restorePreviousContext();

	//! Tries to guess the best separator automagically
	void autoFindBestSeparator();

	//! Sets the current separator
	void setSeparator(QChar);

	//! Sets the number of lines to skip
	void setSkippedLines(int lineCount, bool blockSignal = true);

	//! Resest all column roles
	void resetColumnRoles();

	// associated UI
	Ui_AsciiOpenDialog* m_ui;

	QChar        m_separator;
	double       m_averageLineSize;
	QString      m_filename;
	QTextStream* m_stream;
	QString      m_headerLine;

	enum ColumnType
	{
		TEXT    = 0,
		UNKNOWN = 1,
		IGNORED = 2,
		VALID   = 3
	};

	//! Identifies columns with numbers only [mandatory]
	std::vector<ColumnType> m_columnType;

	unsigned m_columnsCount;
};

#endif // CC_ASCII_OPEN_DIALOG_HEADER
